package com.javaspeak.concurrency.lock.cyclicbarrier;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;

import org.apache.log4j.Logger;

/**
 * This job does not do much. It starts by calling doStuffA() which sleeps a 
 * random amount. When doStuffA() is complete it calls cyclicBarrier.await()
 * which blocks.
 * <p>
 * When cyclicBarrier.await() has been called the same number of times as the
 * number of parties passed through the constructor of the CyclicBarrier then
 * cyclicBarrier.await() unblocks and execution resumes and each of the threads
 * moves on to executing doStuffB().  In a similar fashion they then block on
 * cyclicBarrier.await() again. When all threads have completed doStuffB() then
 * all the threads commence doStuffC().
 * <p>
 * Infact CyclicBarrier is a good way to synchronize multiple threads so that
 * they complete a task and then block until everyone has completed the task
 * before moving onto the next task.
 * <p>
 * This example does not use the feature but if a Runnable is passed in through
 * the constructor of a CyclicBarrier it will be executed once 
 * cyclicBarrier.await() has been called the same number of times as specified 
 * by the number of parties passed through the constructor of CyclicBarrier. This
 * Runnable allows you to process the data all the threads have been working on
 * before they resume execution and do some more work in parallel.
 * 
 * @author John Dickerson
 */
public class Job implements Callable<String> {
    
    public Logger logger = Logger.getLogger( Job.class );
    
    private CyclicBarrier cyclicBarrier;
    private Random random;

    private void doStuffA() throws InterruptedException {
        
        int randomInt = random.nextInt( 3 ) * 1000; 
        Thread.sleep( randomInt );
    }
    
    
    private void doStuffB() throws InterruptedException {
        
        int randomInt = random.nextInt( 3 ) * 1000; 
        Thread.sleep( randomInt );
    }
    
    
    private void doStuffC() throws InterruptedException {
        
        int randomInt = random.nextInt( 3 ) * 1000; 
        Thread.sleep( randomInt );
    }
    
    
    public Job( CyclicBarrier cyclicBarrier, Random random ){
        
        this.cyclicBarrier = cyclicBarrier;
        this.random = random;
    }
    
    
    /* (non-Javadoc)
     * @see java.util.concurrent.Callable#call()
     */
    @Override
    public String call() throws Exception {
 
        try {
            doStuffA();
            
            int arrivalIndex = cyclicBarrier.await();
            
            logger.debug( Thread.currentThread().getName() + 
                  ", completed doStuffA() at arrivalIndex : " + arrivalIndex + 
                      ", now commencing doStuffB()" );
            
            doStuffB();
            
            arrivalIndex = cyclicBarrier.await();
            
            logger.debug( Thread.currentThread().getName() + 
                    ", completed doStuffB() at arrivalIndex : " + arrivalIndex + 
                        ", now commencing doStuffC()" );
            
            doStuffC();
            
            arrivalIndex = cyclicBarrier.await();
            
            logger.debug( Thread.currentThread().getName() + 
                    ", completed doStuffC() at arrivalIndex : " + arrivalIndex + 
                        ", now exiting thread" );
            
            return Thread.currentThread().getName() + ": Success";
        }
        catch( BrokenBarrierException | InterruptedException e ) {
           
            e.printStackTrace();
            logger.error( e );
             
            cyclicBarrier.reset();
            throw e;
        }
        catch( Throwable t  ){
            
            t.printStackTrace();
            logger.error( t );
            
            cyclicBarrier.reset();
            throw t;
        }
    }
}
